/*
 * CApplication.cpp
 *
 *  Created on: 2020年9月16日
 *      Author: chuanjiang.zh
 */

#include "CApplication.h"
#include "CPollTimerQueue.h"
#include "CLog.h"
#include "ClockTime.h"


CApplication::CApplication() :
	m_canExit(),
	m_timerQueue(new comn::CPollTimerQueue()),
	m_exitCode()
{
}

CApplication::~CApplication()
{
	m_timerQueue->clear();
	delete m_timerQueue;
}

void CApplication::post(Task task)
{
	size_t queSize = 0;
	{
		TaskRecord record(getTime(), task);
		std::lock_guard< std::mutex >	lock(m_mutex);
		m_taskQueue.push_back(record);
		queSize = m_taskQueue.size();
	}

	postEvent();

	if (queSize > QUEUE_SIZE)
	{
		CLog::warning("CApplication::post. too many task. size: %d\n", queSize);
	}
}

void CApplication::clearTask()
{
	{
		std::lock_guard< std::mutex >	lock(m_mutex);
		m_taskQueue.clear();
	}

	postEvent();
}

void CApplication::onIdle()
{
	int ms = m_timerQueue->poll();
	if (ms < 0)
	{
		ms = IDLE_DURATION;
	}

	timedwait(ms);
}

int CApplication::run()
{
	while (!m_canExit)
	{
		if (!fetchAndHandle())
		{
			onIdle();
		}
	}

	CLog::info("CApplication::run return. code: %d\n", m_exitCode);

	return m_exitCode;
}

void CApplication::exit(int code)
{
	m_exitCode = code;

	m_canExit = true;

	postEvent();
}


bool CApplication::fetchAndHandle()
{
	TaskRecord record;
	if (!fetchTask(record))
	{
		return false;
	}

	m_invokeWatch.start();

	record.task();

	int elapse = (int)m_invokeWatch.stop();

	if (elapse > OVERRUN_TIME)
	{
		int64_t clock = getTime();
		CLog::warning("CApplication::fetchAndHandle(%s). overrun. task clock: %lld, cur clock: %lld, elapse: %d ms\n",
			record.name.c_str(), record.clock, clock, elapse);
	}

	return true;
}

bool CApplication::fetchTask(TaskRecord& record)
{
	bool got = false;
	std::lock_guard< std::mutex >	lock(m_mutex);
	if (m_taskQueue.size() > 0)
	{
		record = m_taskQueue.front();
		m_taskQueue.pop_front();
		got = true;
	}
	return got;
}

int CApplication::setTimer(uint32_t ms, TimerHandler handler)
{
	int tid = m_timerQueue->addTimer(ms, handler);

	postEvent();
	return tid;
}

int CApplication::setTimeout(uint32_t ms, TimerHandler handler)
{
	int tid = m_timerQueue->addElapse(ms, handler);

	postEvent();
	return tid;
}

void CApplication::killTimer(int tid)
{
	m_timerQueue->cancel(tid);

	postEvent();
}

void CApplication::killTimers()
{
	m_timerQueue->clear();

	postEvent();
}

int64_t CApplication::getTime()
{
	return comn::ClockTime::getTime();
}

bool CApplication::isBlocking(int milliseconds)
{
	int elapse = (int)(m_invokeWatch.elapse());
	return elapse > milliseconds;
}


bool CApplication::isQueueFull(int milliseconds)
{
	int duration = getQueueDuration();
	bool isFull = duration > milliseconds;
	return isFull;
}

bool CApplication::checkQueueFull(int milliseconds, int& curDuration)
{
	curDuration = getQueueDuration();
	bool isFull = curDuration > milliseconds;
	return isFull;
}

int CApplication::getQueueDuration()
{
	int duration = 0;
	std::lock_guard< std::mutex >	lock(m_mutex);
	if (m_taskQueue.size() > 1)
	{
		duration = (int)(m_taskQueue.back().clock - m_taskQueue.front().clock);
	}
	return duration;
}

int CApplication::getQueueSize()
{
	std::lock_guard< std::mutex >	lock(m_mutex);
	return (int)m_taskQueue.size();
}

void CApplication::postEvent()
{
	m_eventTask.post();
}

bool CApplication::timedwait(int ms)
{
	return m_eventTask.timedwait(ms);
}

int64_t CApplication::getCurTaskElapse()
{
	return m_invokeWatch.elapse();
}

void CApplication::postEx(const std::string& name, Task task)
{
	size_t queSize = 0;
	{
		TaskRecord record(getTime(), task);
		record.name = name;

		std::lock_guard< std::mutex >	lock(m_mutex);
		m_taskQueue.push_back(record);
		queSize = m_taskQueue.size();
	}

	postEvent();

	if (queSize > QUEUE_SIZE)
	{
		CLog::warning("CApplication::post. too many task. size: %d\n", queSize);
	}
}